/* eslint-disable no-await-in-loop */
import { Tsrv } from '@/services';
import * as ifs from '../../ifs';

import * as ts from '../index';

import * as tvs from '../../typeValue';
import { T_base } from './base';

const qs = require('qs');
const axios = require('axios');

export interface I_apiReqOption {
  headers?: {
    // 验证头设置
    Authorization?: string;
    // 自定义头Key
    [key: string]: any;
  };
  // 外部请求
  isExternalReq?: boolean;

  [key: string]: any; // options['cancelToken']
}

/**
 * ts.T_data 数据类
 */
export class T_data extends T_base {
  // 标准通用post API
  // Tres 返回对象接口
  // Treq 请求对象接口
  // p : 请求的对象实例，根据请求对象解析参数和请求路径
  // url: 如果请求路径的指定则优先使用url路径
  public async standardPostReq<Tres = any, Treq = any>(
    p: Treq,
    url?: string,
    options: I_apiReqOption = null
  ): Promise<[Tres, string]> {
    return await this.webPost<Tres, Treq>(p, url || '', options);
  }

  // 标准通用get API
  // Tres 返回对象接口
  // Treq 请求对象接口
  // p : 请求的对象实例，根据请求对象解析参数和请求路径
  // url: 如果请求路径的指定则优先使用url路径
  public async standardGetReq<Tres = any, Treq = any>(
    p: Treq,
    url: string = null,
    options: I_apiReqOption = null
  ): Promise<[Tres, string]> {
    return await this.webGet<Tres, Treq>(p, url || '', options);
  }

  /**
   * @description: 请求数据
   * @param
   * 泛型参数：TresRowsObj: 给定的返回结构规范T
   *          Treq: 请求类实例 通常在obj/class/arg里定义
   *        p:   请求的传参类
   *        url: 给定的api请求URL,此参数非必需,如果p为系统obj/class/arg里有定义class。
   *             会取class静态属性所指定的URL
   * @return  [
   *              boolean:成功|失败,
   *              T_queryRes:查询返回类(取axios返回data属性),
   *              TresRow：返回行信息T_queryRes.rows,
   *              msg:成功/失败消息
   *          ]
   * @author: wiz
   * @Date: 2022-12-23 17:24:45
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */

  public async getData<TresRowsObj = any, Treq = any>(
    p: Treq,
    url?: string
  ): Promise<[boolean, ifs.I_queryRes<TresRowsObj>, TresRowsObj[], string]> {
    const [resp, err] = await this.webPost<ifs.I_queryRes<TresRowsObj>, Treq>(
      p,
      url
    );
    if (err) {
      return [false, resp, resp?.data.rows, resp?.msg ? resp?.msg : err];
    }
    if (resp.success) {
      return [true, resp, resp?.data.rows, resp?.msg];
    }
    return [false, resp, resp?.data?.rows, resp?.msg];
  }

  /**
   * @description:获取功能信息
   * @param
   *        funName:主功能名|副功能名
   * @return { Promise<ifs.I_funInfo>}
   * @author: wiz
   * @Date: 2022-12-23 17:46:29
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  public reqGetFunInfo(funName: string): Promise<ifs.I_funInfo> {
    return new Promise((resolve, reject) => {
      this.standardPostReq<ifs.I_setDataRes<ifs.I_funInfo>, any>(
        { funName },
        this.url.getFunInfo
      )
        .then(([r, err]) => {
          if (err || !r.success) {
            console.error(err ?? r.msg);
            resolve(null);
          }
          this.getStore().commit('SET_FUNINFO', {
            key: r.data.mainFun,
            value: r.data
          });
          resolve(r.data);
        })
        .catch(e => {
          console.error('error:', e);
          reject(e);
        });
    });
  }

  /**
   * @description:获取字段附加列表
   * @param
   *        id:字符附加列表表头定位id
   *        lang:当前国际化语言别
   *        dbType:请求的数据库类型
   *        filter 特殊需要的过滤
   * @return {*}
   * @author: wiz
   * @Date: 2022-12-23 17:46:29
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  public async getSLCData(
    id: string,
    dbType: string,
    lang: string,
    filter: any = null
  ): Promise<[boolean, { label: string; value: number }[], string]> {
    interface I_res {
      f_text: string;
      f_value: number;
    }
    const res: { label: string; value: number }[] = [];

    const Treq = new ts.T_GPFD();
    Treq.setAgreedFields('f_text', 'f_value')
      .setIsDistinct(false)
      .setIsPagingQuery(false);
    if (filter) {
      Treq.setFilter({
        $and: [{ _sys_property_type_id: { $eq: id } }, filter]
      });
    } else {
      Treq.setFilter({ _sys_property_type_id: { $eq: id } });
    }

    Treq.setTableName('_sys_property_type_detail')
      .setFunName('_sys_property_type_detail')
      .setLang(lang)
      .setRule({} as ifs.I_rule)
      // .setDbType(dbType); //一定是在mysql
      .setDbType('mysql');

    const [b, _, rows, msg] = await this.getData<I_res, ifs.I_GPFD>(Treq);
    if (b) {
      rows.forEach(r => {
        res.push({ label: r.f_text, value: r.f_value });
      });
    }
    return [b, res, msg];
  }

  // 获得默认值
  public getDefaultVal(defVal: ifs.I_devVal): Promise<ifs.I_fv[]> {
    return new Promise((resolve, reject) => {
      const fvs: Array<ifs.I_fv> = [];
      this.standardPostReq<ifs.I_defValRes, ifs.I_devVal>(defVal)
        .then(([res, msg]) => {
          if (!res.success || !msg) {
            resolve(fvs);
          }
          if (res?.data?.total === 0) {
            resolve(fvs);
          }
          for (const key in res.data.data) {
            fvs.push({ field: key, value: res.data.data[key] } as ifs.I_fv);
          }
          resolve(fvs);
        })
        .catch(r => {
          console.error('error:', r);
          reject(r);
        });
    });
  }

  /**
   * @description:一个带选项的数据查询
   * @param {string}dbType:请求的数据库类型
   * @param {string}tableName:表名 非必需
   * @param {string}funName： 功能名
   * @param {string}lang:   当前语言
   * @param {object}filter:   过滤条件
   * @param {Array<string>}agreedFields:   指定返回字段
   * @return {Promise<[boolean, ifs.I_queryRes<any>, any[], string]}
   *          成功/失败，res,rows,err
   * @author: wiz
   * @Date: 2022-12-23 17:46:29
   * @LastEditors: wiz
   * @LastEditTime: 详细设置参见getFieldDataList.ts
   */
  public async getDataWithOption(
    lang: string,
    dbType: string,
    funName: string,
    agreedFields: Array<string>,
    filter: object,
    tableName?: string
  ): Promise<[boolean, ifs.I_queryRes<any>, any[], string]> {
    const Treq = new ts.T_GPFD();
    Treq.setAgreedFields(...agreedFields);
    Treq.setFilter(filter)
      .setTableName(tableName)
      .setFunName(funName)
      .setLang(lang)
      .setRule({} as ifs.I_rule)
      .setDbType(dbType);
    return this.getData<any, ifs.I_GPFD>(Treq);
  }

  /**
   * @description:查询是否有数据
   * @param {string}dbType:请求的数据库类型
   * @param {string}funName:功能名
   * @param {object}filter:   过滤条件
   * @return {Promise<[boolean, string]}
   *          有/无,err
   * @return {*}
   * @author: wiz
   * @Date: 2022-12-23 17:46:29
   * @LastEditors: wiz
   * @LastEditTime: 详细设置参见getFieldDataList.ts
   */
  public async exists(
    dbType: string,
    funName: string,
    filter: object
  ): Promise<[boolean, string]> {
    const [b, res, rows, err] = await this.getDataWithOption(
      this.globalVar.i18n.enUS,
      dbType,
      funName,
      [this.globalVar.fieldName.ID],
      filter
    );
    if (!err) {
      return [false, err];
    }
    if (!b) {
      return [false, res.msg];
    }
    return [rows.length > 0, ''];
  }

  /**
   * @description: 获取自动编号 （当funName为空时funID有效，当funName为空时funID无效）
   * @param {string} funName 功能名
   * @param {string} fieldName 字段名
   * @param {string} funID  功能ID（可选值）
   * @return {*}
   * @author: swc
   * @Date: 2024-06-01 15:37:51
   * @LastEditors: swc
   * @LastEditTime: Do not edit
   */
  public async getAutoNum(funName: string, fieldName: string, funID?: string) {
    const params = {
      funID: funID ?? '1',
      fieldName,
      params: {
        dbType: this.globalVar.dbType.MYSQL,
        funName,
        userID: this.getUserID()
      }
    };
    // return this.standardPostReq<ifs.I_setDataRes, any>(params, this.url.gan);
    const [resp, err] = await this.webPost<any, any>(params, this.url.gan);
    return [resp, err];
  }

  // =======================changeTableDataApi========================================================

  //  ctd 新增
  // tableName 表名 // dbType 数据库类型 rule 规则  p ts.T_ctdInsRec
  public async addRows(
    tableName: string,
    dbType: string,
    rule: ifs.I_rule,
    p: ifs.I_ctdInsRec
  ): Promise<[boolean, ifs.I_setDataRes, any]> {
    if (this.utils.noValue(tableName) || !p) {
      return [false, null, `-507,FE,50,Invalid parameter!->tableName|p`];
    }
    const ctd = new ts.T_ctdArg();
    ctd.setTableName(tableName);
    ctd.setDbType(dbType);
    ctd.setRule(rule);
    ctd.insertRecords = p;
    return await this.setData<ifs.I_ctdArg>(ctd);
  }

  //  ctd 修改
  public async updRows(
    tableName: string,
    dbType: string,
    rule: ifs.I_rule,
    p: ifs.I_ctdUpdRec
  ): Promise<[boolean, ifs.I_setDataRes, string]> {
    if (this.utils.noValue(tableName) || !p) {
      return [false, null, `-507,FE,50,Invalid parameter!->tableName|p`];
    }
    const ctd = new ts.T_ctdArg();
    ctd.setTableName(tableName);
    ctd.setDbType(dbType);
    ctd.setRule(rule);
    ctd.updateRecords = p;
    return await this.setData<ifs.I_ctdArg>(ctd);
  }

  //  ctd 删除
  public async delRows(
    tableName: string,
    dbType: string,
    rule: ifs.I_rule,
    p: ifs.I_ctdDelRec
  ): Promise<[boolean, ifs.I_setDataRes, string]> {
    if (this.utils.noValue(tableName) || !p) {
      return [false, null, `-507,FE,50,Invalid parameter!->tableName`];
    }
    const ctd = new ts.T_ctdArg();
    ctd.setTableName(tableName);
    ctd.setDbType(dbType);
    ctd.setRule(rule);
    ctd.removeRecords = p;

    return await this.setData<ifs.I_ctdArg>(ctd);
  }

  //  ctd 混合处理
  public async batRows(
    p: ifs.I_ctdArg
  ): Promise<[boolean, ifs.I_setDataRes, string]> {
    return await this.setData<ifs.I_ctdArg>(p);
  }

  // 导出excel
  public expExcel(
    fileName: string,
    dbType: string,
    funName: string,
    lang: string,
    fields: string[],
    filter: object,
    sort: ifs.I_sort[],
    globalSearch?: string
  ): Promise<[boolean, string]> {
    return new Promise(resolve => {
      const noValue = this.utils.noValue;
      const params = new ts.T_expExcel();
      params
        .setFileName(fileName)
        .setDbType(dbType)
        .setFunName(funName)
        .setLang(lang);
      params
        .setFields(...fields)
        .setSort(...sort)
        .setFilter(filter);
      if (!noValue(globalSearch)) {
        params.setGllobalSearch(globalSearch);
      }

      this.standardPostReq<
        ifs.I_setDataRes<{ path: string; fileName: string }>,
        ifs.I_expExcel
      >(params).then(r => {
        const [res, err] = r;
        if (!noValue(err) || !res.success) {
          console.error(err ?? res.msg);
          resolve([false, err ?? res.msg]);
          return;
        }
        if (noValue(res.data.fileName) || noValue(res.data.fileName)) {
          resolve([false, '50007, FE, 90, No file Name']);
          return;
        }
        this.standardGetReq(
          { fileName: res.data.fileName, path: res.data.path },
          this.url.download,
          {
            headers: {
              Authorization: `Bearer ${this.cookies.getToken()}`
            },
            responseType: 'blob' // 设置响应类型为 blob
          }
        ).then(([blob, er]) => {
          const _url = URL.createObjectURL(blob);
          fileName += '.xlsx';
          const link = document.createElement('a');
          link.setAttribute('download', fileName);
          link.style.display = 'none';
          link.href = _url;
          document.body.appendChild(link);
          link.click();
          document.body.removeChild(link);
          URL.revokeObjectURL(_url); // 销毁
          resolve([true, '']);
        });
      });
    });
  }

  // /**
  //  * @description:下载文件
  //  * @param {ts.T_downloadArg}p:下载请求实例
  //  * @param {boolean}downLoadTodisk:是否直接下载到磁盘
  //  * @param {string}fileName:下载后的文件名 默认为空。为空则会取文件库的文件名
  //  * @return {Promise<{ url: string; err: string; revokeObjectURL: () => void }>}
  //  *          1. 当downLoadTodisk=true 返回无意义
  //  *          2. 当downLoadTodisk=false \
  //  *             返回{url 临时下载的url,err错误 revokeObjectURL下载完成释放url资源的回调方法}
  //  *          3. 当发生错误时触发 reject
  //  * @author: wiz
  //  * @Date: 2022-12-27 17:46:29
  //  * @LastEditors: wiz
  //  */
  // public download(
  //   p: ts.T_downloadArg,
  //   downLoadTodisk: boolean = false,
  //   fileName: string = undefined
  // ): Promise<{ url: string; err: string; revokeObjectURL: () => void }> {
  //   const noValue = this.utils.noValue;
  //   return new Promise((resolve, reject) => {
  //     this.standardGetReq<any, ifs.I_downloadArg>(p, undefined, {
  //       headers: {
  //         Authorization: `Bearer ${this.cookies.getToken()}`
  //       },
  //       responseType: 'blob' // 设置响应类型为 blob
  //     }).then(([blob, er]) => {
  //       if (er) {
  //         reject({ url: null, err: er });
  //       }
  //       const _url = URL.createObjectURL(blob);
  //       const removeURL = () => {
  //         URL.revokeObjectURL(_url); // 销毁
  //       };
  //       if (downLoadTodisk) {
  //         const link = document.createElement('a');
  //         link.setAttribute('download', fileName ?? p.getFileName());
  //         link.style.display = 'none';
  //         link.href = _url;
  //         document.body.appendChild(link);
  //         link.click();
  //         document.body.removeChild(link);
  //         removeURL();
  //         resolve({ url: null, err: null, revokeObjectURL: null });
  //         return;
  //       }
  //       resolve({ url: _url, err: er, revokeObjectURL: removeURL });
  //     });
  //   });
  // }
  public async download(
    p: ts.T_downloadArg,
    downLoadTodisk: boolean = false,
    fileName: string = undefined,
    progress: boolean = false
  ): Promise<{ url: string; err: string; revokeObjectURL: () => void }> {
    const noValue = this.utils.noValue;
    const resp = await this.doDownload(p.getFileName(), p.getPath(), progress);
    if (resp.isErr()) {
      return { url: null, err: resp.errmsg, revokeObjectURL: null };
    }
    const _url = URL.createObjectURL(resp.getReturnData('data'));
    const removeURL = () => {
      URL.revokeObjectURL(_url); // 销毁
    };
    if (downLoadTodisk) {
      const link = document.createElement('a');
      link.setAttribute('download', fileName ?? p.getFileName());
      link.style.display = 'none';
      link.href = _url;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
      removeURL();
      return { url: null, err: null, revokeObjectURL: null };
    }
    return { url: _url, err: resp.errmsg, revokeObjectURL: removeURL };
  }

  private async doDownload(
    fileName: string,
    path: string,
    progress: boolean = false
  ): Promise<ifs.I_return<Blob>> {
    const res = await axios
      .get(this.lexicon.url.downloadStatus, {
        params: { fileName, path },
        headers: {
          Authorization: `Bearer ${this.cookies.getToken()}`
        }
      })
      .catch(err => {
        console.error('error:', err);
        return new ts.T_return(err.message ?? err.toString());
      });
    const CHUNK_SIZE = 5 << 20;
    if (res.data.code !== 200) {
      return new ts.T_return(res.data.message);
    }
    const fileSize = res.data.data.size;

    let received = 0;
    // 进度条展示
    const maxChunksLength = Math.ceil(fileSize / CHUNK_SIZE);
    const steps = [];
    for (let i = 1; i < maxChunksLength + 1; i++) {
      steps.push({
        name: `Chunk ${i}`,
        status: 'waiting'
      });
    }
    const pm = {
      modelCf: {
        title: `${fileName}`
      },
      autoClose: true,
      config: {
        percentage: 0,
        steps: []
      }
    };
    if (progress && maxChunksLength > 1) {
      this.utils
        .createModal('o-progress-bar', pm)
        .then(r => {
          // 进度条关闭后
        })
        .catch(e => {
          console.error(e);
        });
    }
    const chunks: Blob[] = [];
    while (received < fileSize) {
      const end = Math.min(received + CHUNK_SIZE - 1, fileSize - 1);
      // eslint-disable-next-line no-await-in-loop
      const chunk = await axios
        .get(this.lexicon.url.download, {
          responseType: 'blob',
          headers: {
            Range: `bytes=${received}-${end}`,
            Authorization: `Bearer ${this.cookies.getToken()}`
          },
          params: { fileName, path }
        })
        .catch(err => {
          console.error('error:', err);
          return new ts.T_return<string>(err.toString());
        });
      chunks.push(chunk.data);
      received += CHUNK_SIZE;
      pm.config.percentage = parseFloat(
        ((received / fileSize) * 100).toFixed(2)
      );
      pm.config.steps = steps.map((_, i) => {
        return {
          name: _.name,
          status: i + 1 <= chunks.length ? 'success' : 'waiting'
        };
      });
    }
    const blob = new Blob(chunks);
    const r = new ts.T_return<Blob>();
    r.setReturnData('data', blob);
    return r;
  }

  // public upLoad(p: ifs.I_uploadArg): Promise<[ifs.I_setDataRes<any>, string]> {
  //   const opts: I_apiReqOption = {
  //     headers: {
  //       'Content-Type': 'multipart/form-data'
  //     }
  //   };
  //   return this.standardPostReq<ifs.I_setDataRes, ifs.I_uploadArg>(
  //     p,
  //     undefined,
  //     opts
  //   );
  // }
  /**
   * @description 上传
   * @param p 上传参数
   * @param progress ：是否使用进度调
   * @description: 优化项：1、上传的chunk的加入进度
   */
  public async upLoad(
    p: ifs.I_uploadArg,
    progress: boolean = false
  ): Promise<[ifs.I_setDataRes<any>, string]> {
    const resp = await this.doUpload(p, progress).catch(err => {
      console.error('error:', err);
      return new ts.T_return(err.message ?? err.toString());
    });
    if (resp.isErr()) {
      const r = new ts.T_setDataRes<any>();
      r.success = false;
      r.msg = resp.errmsg;
      return [r, null];
    }
    const r = new ts.T_setDataRes<
      Array<{
        sourceFileName: string;
        targetFileName: string;
        totalSize: number;
      }>
    >();
    r.data = resp.getReturnData('data');
    r.success = true;
    r.msg = this.lexicon.msg.success;
    return [r, null];
  }

  private async doUpload(
    p: ifs.I_uploadArg,
    progress: boolean = false
  ): Promise<
    ifs.I_return<
      Array<{
        sourceFileName: string;
        targetFileName: string;
        totalSize: number;
      }>
    >
  > {
    const fs = p.getFiles();
    const resp: Array<{
      sourceFileName: string;
      targetFileName: string;
      totalSize: number;
    }> = [];
    if (!p.chkParams()) {
      return new ts.T_return('-507,FE,50,Invalid parameter');
    }
    let funInfo = p.getFunInfo();
    let makeFunInfo: boolean = false;
    if (!Array.isArray(funInfo)) {
      makeFunInfo = true;
      funInfo = [];
    }

    // 计算上传进度
    const calculateUploadPercentage = (
      uploadedChunks: number,
      totalChunks: number
    ) => {
      // 处理边界情况：总块数为0时返回0%
      if (totalChunks === 0) {
        return 0;
      }
      // 确保已上传块数不超过总块数
      const safeUploadedCount = Math.min(uploadedChunks, totalChunks);
      // 计算百分比并保留两位小数
      // const percentage = (safeUploadedCount / totalChunks) * 100;
      const percentage = (safeUploadedCount / totalChunks) * 90 + 10;
      return parseFloat(percentage.toFixed(2)); // 四舍五入并转为数字类型
    };

    // 修改上传进度参数
    const setUploadProgress = (
      pm: any,
      uploadedChunks: Array<any>,
      chunks: Array<any>
    ) => {
      const progressSteps = chunks.map((_, i) => {
        return {
          name: `${Tsrv.getI18nBySrv('chunk')}${i + 1}`,
          status: uploadedChunks.includes(i) ? 'success' : 'waiting'
        };
      });
      progressSteps.unshift({
        name: Tsrv.getI18nBySrv('file_ready'),
        status: 'success'
      });
      pm.config.steps = progressSteps;
      pm.config.percentage = calculateUploadPercentage(
        uploadedChunks.length,
        chunks.length
      );
    };

    for (let j = 0; j < fs.length; j++) {
      const file = fs[j];
      if (!file) continue;
      if (makeFunInfo) {
        funInfo.push({
          funName: '',
          fileName: file.name
        });
      }
      // 进度条展示
      const pm = {
        modelCf: {
          title: `${file.name}`
        },
        autoClose: true,
        config: {
          percentage: 0,
          steps: [
            {
              name: `${Tsrv.getI18nBySrv('file_ready')}...`,
              status: 'waiting'
            }
          ]
        }
      };
      const fileMd5Resp = await this.utils.calculateFileSHA256(file);
      if (fileMd5Resp.isErr()) {
        return fileMd5Resp;
      }
      const fileMd5 = this.utils.md5(fileMd5Resp.getReturnData('data'));
      const chunks = this.splitFile(file);

      // 文件准备就绪
      pm.config.steps = [
        {
          name: Tsrv.getI18nBySrv('file_ready'),
          status: 'success'
        }
      ];
      pm.config.percentage = 10;

      const usResp = await axios
        .get(this.lexicon.url.uploadStatus, {
          params: { file_md5: fileMd5 }
        })
        .catch(err => {
          console.error('error:', err);
          return new ts.T_return(err.message ?? err.toString());
        });
      if (usResp instanceof ts.T_return) {
        if (usResp.isErr()) {
          return usResp;
        }
      }
      if (!usResp.data.success) {
        console.log(usResp.data.msg);
      }
      const uploadedChunks: number[] = usResp.data.data;
      const mergeP = {
        file_md5: fileMd5,
        total_chunks: chunks.length,
        fileName: file.name,
        params: {
          type: p.getType(),
          wsCliID: p.getWsCliID(),
          routeID: p.getRouteID(),
          dbName: this.getDBName()
        }
      };

      setUploadProgress(pm, uploadedChunks, chunks);
      if (progress && chunks.length > 1) {
        this.utils
          .createModal('o-progress-bar', pm)
          .then(r => {
            // 进度条关闭后
            if (uploadedChunks.length < chunks.length) {
              // throw new Error('上传终止');
            }
          })
          .catch(e => {
            console.error(e);
          });
      }

      for (let i = 0; i < chunks.length; i++) {
        if (!uploadedChunks.includes(i)) {
          const formData = new FormData();
          formData.append('file', chunks[i]);
          formData.append('index', i.toString());
          formData.append('file_md5', fileMd5);
          formData.append('fileName', file.name);
          formData.append('dbName', this.getDBName());
          const ucResp = await axios
            .post(this.lexicon.url.uploadChunk, formData, {
              headers: {
                'Content-Type': 'multipart/form-data'
              }
            })
            .catch(err => {
              console.error('error:', err);
              return new ts.T_return(err.message ?? err.toString());
            });
          if (ucResp instanceof ts.T_return) {
            if (ucResp.isErr()) {
              return ucResp;
            }
          }
          if (!ucResp.data.success) {
            return new ts.T_return(ucResp.data.msg);
          }
          uploadedChunks.push(i);
          if (i !== chunks.length - 1) {
            setUploadProgress(pm, uploadedChunks, chunks);
          }
        }
        // 最后一个文件的最后一块合并的同时处理文件库信息
        if (j === fs.length - 1) {
          mergeP.params['funInfo'] = funInfo;
          mergeP.params['lang'] = this.getSysDefLang();
          mergeP.params['dataHandle'] = p.getDataHandle()
            ? p.getDataHandle()
            : null;
          mergeP.params['option'] = p.getOption();
          mergeP.params['mode'] = p.getMode();
        }
        // 文件的最后一块合并文件
        if (i === chunks.length - 1) {
          const upmResp = await axios
            .post(this.lexicon.url.uploadMerge, mergeP, {
              headers: {
                'Content-Type': 'application/json'
              }
            })
            .catch(err => {
              console.error('error:', err);
              return new ts.T_return(err.message ?? err.toString());
            });
          if (upmResp instanceof ts.T_return) {
            if (upmResp.isErr()) {
              return upmResp;
            }
          }
          if (!upmResp.data.success) {
            return new ts.T_return(upmResp.data.msg);
          }
          setUploadProgress(pm, uploadedChunks, chunks);
          upmResp.data.data.forEach(ele => {
            resp.push({
              sourceFileName: ele.sourceFileName,
              targetFileName: ele.targetFileName,
              totalSize: ele.totalSize
            });
          });
        }
      }
    }
    return new ts.T_return().setReturnData('data', resp);
  }

  // 拆分文件
  private splitFile(file: File): Blob[] {
    const CHUNK_SIZE = 5 << 20;
    const chunks: Blob[] = [];
    let current = 0;
    while (current < file.size) {
      chunks.push(file.slice(current, current + CHUNK_SIZE));
      current += CHUNK_SIZE;
    }
    return chunks;
  }

  // =======================basePanelApi========================================================
  //  bpa 新增单据
  private setBpa(
    bpa: ts.T_bpaArg,
    p: ifs.I_addbpaArg | ifs.I_updbpaArg | ifs.I_delbpaArg
  ): ifs.I_return {
    const err = new ts.T_return('');
    if (p.head) {
      if (this.utils.noValue(p.head.funName)) {
        return err.error(`-507,FE,50,Invalid parameter!->funName`);
      }
      if (this.utils.noValue(p.head.dbType)) {
        return err.error(`-507,FE,50,Invalid parameter!->dbType`);
      }
      bpa.head.setTableName(p.head.funName);
      bpa.head.setDbType(p.head.dbType);
      bpa.head.setRule(p.head.rule);
      if (p instanceof ts.T_addbpaArg) {
        if (p.head.insertRecords) {
          bpa.head.insertRecords = p.head.insertRecords;
          if (bpa.head?.updateRecords) {
            bpa.head.updateRecords = undefined;
          }
          if (bpa.head?.removeRecords) {
            bpa.head.removeRecords = undefined;
          }
        }
      } else if (p instanceof ts.T_updbpaArg) {
        if (p.head.updateRecords) {
          bpa.head.updateRecords = p.head.updateRecords;

          if (bpa.head?.insertRecords) {
            bpa.head.insertRecords = undefined;
          }
          if (bpa.head?.removeRecords) {
            bpa.head.removeRecords = undefined;
          }
        }
      } else if (p instanceof ts.T_delbpaArg) {
        if (p.head.removeRecords) {
          bpa.head.removeRecords = p.head.removeRecords;
          if (bpa.head?.insertRecords) {
            bpa.head.insertRecords = undefined;
          }
          if (bpa.head?.updateRecords) {
            bpa.head.updateRecords = undefined;
          }
        }
      } else {
        return err.error(`-106,FE,50,Undefined type!`);
      }
    }
    if (p.body) {
      if (this.utils.noValue(p.body?.data)) {
        bpa.body = undefined;
        return err;
      }
      if (Array.isArray(p.body.data)) {
        if (p instanceof ts.T_addbpaArg) {
          for (const k of p.body.data) {
            if (this.utils.noValue(k.tableName)) {
              return err.error(`-507,FE,50,Invalid parameter!->tableName`);
            }
            if (this.utils.noValue(k.dbType)) {
              return err.error(`-507,FE,50,Invalid parameter!->dbType`);
            }
            bpa.body.rule = p.body.bodyRule;
            if (k.insertRecords) {
              bpa.body.data.push(k);
            }
          }
        } else if (p instanceof ts.T_updbpaArg) {
          for (const k of p.body.data) {
            if (this.utils.noValue(k.tableName)) {
              return err.error(`-507,FE,50,Invalid parameter!->tableName`);
            }
            if (this.utils.noValue(k.dbType)) {
              return err.error(`-507,FE,50,Invalid parameter!->dbType`);
            }
            bpa.body.rule = p.body.bodyRule;
            if (k.updateRecords) {
              bpa.body.data.push(k);
            }
          }
        } else if (p instanceof ts.T_delbpaArg) {
          for (const k of p.body.data) {
            if (this.utils.noValue(k.tableName)) {
              return err.error(`-507,FE,50,Invalid parameter!->tableName`);
            }
            if (this.utils.noValue(k.dbType)) {
              return err.error(`-507,FE,50,Invalid parameter!->dbType`);
            }
            bpa.body.rule = p.body.bodyRule;
            if (k.removeRecords) {
              bpa.body.data.push(k);
            }
          }
        } else {
          return err.error(`-106,FE,50,Undefined type!`);
        }
      }
    }
    return err;
  }

  private async bpaExec(
    p: ifs.I_addbpaArg | ifs.I_updbpaArg | ifs.I_delbpaArg
  ): Promise<[boolean, ifs.I_setDataRes, string]> {
    const bpa = new ts.T_bpaArg();
    const err = this.setBpa(bpa, p);
    if (err.errmsg !== '') {
      return [false, null, err.errmsg];
    }
    return await this.setData<ifs.I_bpaArg>(bpa);
  }

  public async addBill(
    p: ifs.I_addbpaArg
  ): Promise<[boolean, ifs.I_setDataRes, string]> {
    return this.bpaExec(p);
  }

  //  bpa 修改
  public async updBill(
    p: ifs.I_updbpaArg
  ): Promise<[boolean, ifs.I_setDataRes, string]> {
    return this.bpaExec(p);
  }

  //  bpa 删除
  public async delBill(
    p: ifs.I_delbpaArg
  ): Promise<[boolean, ifs.I_setDataRes, string]> {
    return this.bpaExec(p);
  }

  //  bpa 混合
  public async batBill(
    p: ifs.I_bpaArg
  ): Promise<[boolean, ifs.I_setDataRes, string]> {
    if (p.head?.insertRecords) {
      if (this.utils.noValue(p.head.insertRecords?.rowData)) {
        p.head.insertRecords = undefined;
      }
      if (this.utils.noValue(p.head.updateRecords?.rowData)) {
        p.head.updateRecords = undefined;
      }
      if (
        this.utils.noValue(p.head.removeRecords?.mto) ||
        this.utils.noValue(p.head.removeRecords?.filter)
      ) {
        p.head.removeRecords = undefined;
      }
      if (this.utils.noValue(p?.body?.data)) {
        p.body = undefined;
      }
    }
    return await this.setData<ifs.I_bpaArg>(p);
  }

  // 获取系统数据并存store Promise<[Tres, string]>
  public async getSysInfo(
    key: string = null
  ): Promise<[ifs.I_sysInfoRes, string]> {
    let opts: I_apiReqOption = null;
    if (key) {
      opts = {
        Headers: {
          Authorization: `Basic ${key}`
        },
        isExternalReq: true
      };
    }
    return this.standardPostReq<ifs.I_sysInfoRes, any>(
      {},
      this.lexicon.url.getSysInfo,
      opts
    );
  }

  public async ruleQuery<TresRow = any>(
    tableName: string,
    fields: Array<{ [key: string]: tvs.V_01 }> = [],
    filter: object = {},
    pageSize: number = 999999999,
    pageNo: number = 0,
    sort: Array<{ [key: string]: tvs.V_1 }> = []
  ): Promise<[boolean, any, TresRow[], string]> {
    const req = new ts.T_ruleReq();
    req.setTableName(tableName);
    req.setType('query');
    req.params.filter = filter;
    req.params.options = {
      fields,
      sort,
      pageSize,
      pageNo
    };
    return this.getData<TresRow, ifs.I_ruleReq>(req);
  }

  /* 更新
{
  type:"modify" // new modify delete query
  tableName:""
  params:{
    filter:{"$and":[{},{}]},
      options:{}
    data:{
      "$set":{"a":1,"a.b":2}
    }
  }
} 

//新增
{
  type:"new" // new modify delete query
  tableName:""
  params:{
    filter:{},
      options:{}
    data:{
      arr:[
        {},{}
      ]
    }
  }
}
*/
  // 新增

  public async ruleNew(
    tableName: string,
    data: { [key: string]: any }
  ): Promise<[boolean, ifs.I_setDataRes, string]> {
    const req = new ts.T_ruleReq();
    req.setTableName(tableName);
    req.setType('new');
    req.params.data = data;
    return this.setData<ifs.I_ruleReq>(req);
  }

  // 更新
  /* {
    type:"modify" // new modify delete query
    tableName:""
    params:{
      filter:{"$and":[{},{}]},
        options:{}
      data:{
        "$set":{"a":1,"a.b":2}
      }
    }
  }
  */

  // 更新
  public async ruleModify(
    tableName: string,
    data: { [key: string]: any },
    filter: object = {}
  ): Promise<[boolean, ifs.I_setDataRes, string]> {
    const req = new ts.T_ruleReq();
    req.setTableName(tableName);
    req.setType('modify');
    req.params.data = data;
    req.params.filter = filter;
    return this.setData<ifs.I_ruleReq>(req);
  }

  /*
//删除
{
  type:"delete" // new modify delete query
  tableName:""
  params:{
    filter:{"$and":[{},{}]},
      options:{}
    data:{}
  }
}
*/
  public async ruleDelete(
    tableName: string,
    filter: object = {}
  ): Promise<[boolean, ifs.I_setDataRes, string]> {
    const req = new ts.T_ruleReq();
    req.setTableName(tableName);
    req.setType('delete');
    req.params.filter = filter;
    return this.setData<ifs.I_ruleReq>(req);
  }

  //= =============================内部方法====================================
  //  统一调API返回封装
  private async setData<Treq>(
    p: Treq,
    url?: string
  ): Promise<[boolean, ifs.I_setDataRes, string]> {
    const [res, err] = await this.webPost<ifs.I_setDataRes, Treq>(p, url);
    if (err) {
      return [false, res, err];
    }
    return [res?.success, res, res?.msg];
  }

  //  post
  private async webPost<Tres, Treq>(
    p: Treq,
    url: string = null,
    options: I_apiReqOption = null
  ): Promise<[Tres, string]> {
    const resp = await this.httpPost<Treq>(p, url, options);
    if (!resp) {
      return [undefined as Tres, `-507,FE,50,Invalid parameter![webPost]`];
    }
    const d = resp?.data?.data;
    d &&
      typeof d === 'string' &&
      this.utils.isJSONString(d) &&
      (resp.data.data = JSON.parse(d));
    return [resp?.data as Tres, undefined];
  }

  //  get
  private async webGet<Tres, Treq>(
    p: Treq,
    url?: string,
    options: I_apiReqOption = null
  ): Promise<[Tres, string]> {
    const resp = await this.httpGet<Treq>(p, url, options);
    if (!resp) {
      return [undefined as Tres, `-507,FE,50,Invalid parameter![webGet]`];
    }
    // if (options?.responseType === 'blob') {
    //   return resp;
    // }
    const d = resp?.data?.data;
    d &&
      typeof d === 'string' &&
      this.utils.isJSONString(d) &&
      (resp.data.data = JSON.parse(d));
    return [resp?.data as Tres, undefined];
  }

  // http post 请求
  // T 请求对象
  // p : 请求的对象，根据请求对象解析参数和请求路径
  // url: 如果请求路径的指定则优先使用url路径
  private async httpPost<Treq>(
    p: Treq,
    url: string = null,
    options: I_apiReqOption = null
  ): Promise<any> {
    const _p: Treq = this.XEUtils.clone(p, true);
    _p['cancel'] && delete _p['cancel'];
    _p['url'] && delete _p['url'];
    let d: any = null;
    if (options && options?.isExternalReq) {
      d = _p;
    } else {
      d = this.getReqFmt<Treq>(_p);
    }

    if (!d) {
      return undefined;
    }
    if (!url) {
      url = p['url'];
      if (!url) {
        console.error('Failed to get url through data type');
        return undefined;
      }
    }
    if (!options) {
      options = {};
    }
    options['cancelToken'] = p[`cancel`] || undefined;
    return await axios.post(url, d, options);
  }

  // http get 请求
  // T 请求对象
  // p : 请求的对象，根据请求对象解析参数和请求路径
  // url: 如果请求路径的指定则优先使用url路径
  private async httpGet<Treq>(
    p: Treq,
    url: string = null,
    options: I_apiReqOption = null
  ): Promise<any> {
    const _p: Treq = this.XEUtils.clone(p, true);
    _p['cancel'] && delete _p['cancel'];
    _p['url'] && delete _p['url'];
    const d = this.getReqFmt<Treq>(_p, 'external');

    if (!url) {
      url = p['url'];
      if (!url) {
        console.error('Failed to get url through data type');
        return undefined;
      }
    }
    let _url = url;
    if (d) {
      _url = `${url}?${d}`;
    }
    if (!options) {
      options = {};
    }
    options['cancelToken'] = p[`cancel`] || undefined;
    return await axios.get(_url, options);
  }

  //  组装post传参
  private getReqFmt<T>(
    p: T,
    typ: 'internal' | 'external' = 'internal'
  ): string {
    if (typ === 'internal') {
      return this.internalApi<T>(p);
    }
    if (typ === 'external') {
      return this.externalApi<T>(p);
    }
    return qs.stringify(p);
  }

  private externalApi<T>(p: T): string {
    return qs.stringify(p);
  }

  private internalApi<T>(p: T): any {
    if (p instanceof ts.T_getPartDataArg) {
      return qs.stringify({
        dbName: this.getDBName(),
        params: JSON.stringify(p)
      });
    }
    if (p instanceof ts.T_loginReq) {
      const r = wGetKey();
      if (r.isErr()) {
        throw new Error(r.getErrMsg());
      }
      const _pr = wAesEncrypt(
        JSON.stringify(p.params),
        r.getReturnData('data')
      );
      if (_pr.isErr()) {
        throw new Error(r.getErrMsg());
      }
      const _p = _pr.getReturnData('data');

      return qs.stringify({
        dbName: p.dbName,
        params: _p
      });
    }

    if (p instanceof ts.T_getSnowIDArg) {
      return qs.stringify({
        dbName: this.getDBName(),
        params: JSON.stringify({})
      });
    }
    if (p instanceof ts.T_uploadArg) {
      const files = this.XEUtils.clone(p['files'], true);
      const formData = new FormData();
      for (let i = 0; i < files.length; i++) {
        formData.append(`files`, files[i]);
      }
      delete p['files'];
      formData.append('dbName', this.getDBName());
      formData.append('params', JSON.stringify(p));
      return formData;
    }

    // 以下是默认返回,有特殊的在上面截断
    return qs.stringify({
      dbName: this.getDBName(),
      params: JSON.stringify(p)
    });
  }
}

export interface I_data extends T_data {}
